//@author A0092201X
/***********************************************************************************************************************************************
This main.cpp file contains the main function, which is the entry point of the Task Assistant CLI (TAC) program, and acts as the initialiser.
TAC is a CLI program that enables users to manage their tasks in the form of a .txt text file.
This current version has the following 4 functionalities:
1) (Create) add <task>: Enables users to add a task to the .txt text file task list
2) (Retrieve) show <type> <date/time>: Enables users to retrieve tasks from the .txt text file task list based on input <type> and <date/time>
3) (Update) edit <task index> <attribute> <new content>: Enables users to edit an attribute of a task in the .txt text file task list
4) (Delete) del <task index>: Enables users to delete a task from the .txt text file task list
5) (Done/Undone) done/undo <task index>: Enables users to mark a task as done or undone
6) (Undo/Redo) undo/redo: Enables users to undo/redo last action
*************************************************************************************************************************************************/

#include "UserInterface.h"
#include "Logic.h"
#include "Manipulator.h"
#include "Exception.h"

#include "Windows.h"
#include <string>
#include <iostream>
#include <ctime>

//Default file names constant string declarations:
const std::string FILENAME_TODODEFAULT = "TAC_todo.txt";
const std::string FILENAME_DONEDEFAULT = "TAC_done.txt";

//Defaulf log file name constant string declaration:
const std::string FILENAME_LOGDEFAULT = "ActivityLog.txt";

//Welcome messages constant string declarations:
const int NOFILENAMEENTERED = 1; 
const int TODOFILENAMEENTERED = 2;
const std::string MESSAGE_WELCOME = "Welcome to TAC - your friendly Todo Assistant!\n";
const std::string MESSAGE_TODODEFAULTFILENAMECONFIRMATION = "Working on default todo file:";
const std::string MESSAGE_DONEDEFAULTFILENAMECONFIRMATION = ", and default done file:";
const std::string MESSAGE_TODOSPECIFIEDFILENAMECONFIRMATION = "Working on specified todo file:";
const std::string MESSAGE_DONESPECIFIEDFILENAMECONFIRMATION = ", and specified done file:";

const HANDLE HANDLE_STD_OUT = GetStdHandle(STD_OUTPUT_HANDLE);

void formatConsole(HANDLE hConsole);
void revertConsoleColour(HANDLE hConsole);
void resizeConsole(HANDLE, SHORT, SHORT);
void fillConsoleColour(HANDLE, WORD, int, int, int);
void clearScreen(HANDLE);
void printWelcomeMessage(std::string, std::string);
void prestartCheck(Manipulator*);


void main(int numOfArg, char* argVector[]) {
	std::string todoFileName;
	std::string doneFileName;

	if(numOfArg == NOFILENAMEENTERED) {
		todoFileName = FILENAME_TODODEFAULT;
		doneFileName = FILENAME_DONEDEFAULT;
	} else if(numOfArg == TODOFILENAMEENTERED) {
		todoFileName = argVector[1];
		doneFileName = FILENAME_DONEDEFAULT;
	} else {
		todoFileName = argVector[1];
		doneFileName = argVector[2];
	}

	formatConsole(HANDLE_STD_OUT);

	Manipulator* todoTMPtr;
	Manipulator* doneTMPtr;
	Logic* logPtr;

	todoTMPtr = new TextManipulator(todoFileName);
	doneTMPtr = new TextManipulator(doneFileName);

	prestartCheck(todoTMPtr);
	prestartCheck(doneTMPtr); 
	printWelcomeMessage(todoFileName, doneFileName);

	logPtr = new Logic(todoTMPtr, doneTMPtr);
	UserInterface UIObj (logPtr);

	UIObj.run();

	delete todoTMPtr;
	delete doneTMPtr;
	delete logPtr;

	system("pause");

	revertConsoleColour(HANDLE_STD_OUT);
	return;
}

void printWelcomeMessage(std::string todoFileName, std::string doneFileName) {
	std::cout << MESSAGE_WELCOME;

	if(todoFileName == FILENAME_TODODEFAULT) {
		std::cout << "[" << MESSAGE_TODODEFAULTFILENAMECONFIRMATION << FILENAME_TODODEFAULT;
	} else {
		std::cout << "[" << MESSAGE_TODOSPECIFIEDFILENAMECONFIRMATION << todoFileName;
	}

	if(doneFileName == FILENAME_DONEDEFAULT) {
		std::cout << MESSAGE_DONEDEFAULTFILENAMECONFIRMATION << FILENAME_DONEDEFAULT << "]\n\n";
	} else {
		std::cout << MESSAGE_DONESPECIFIEDFILENAMECONFIRMATION << doneFileName << "]\n\n";
	}

	return;
}

void formatConsole(HANDLE hConsole) {
	resizeConsole(hConsole, CONSOLE_WIDTH, CONSOLE_HEIGHT);
	clearScreen(hConsole);

	WORD charAttributes = BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_INTENSITY;
	int size = CONSOLE_WIDTH * CONSOLE_HEIGHT;
	fillConsoleColour(hConsole, charAttributes, size, 0 , 0);
	SetConsoleTextAttribute(hConsole, charAttributes);
}

void revertConsoleColour(HANDLE hConsole) {
	clearScreen(hConsole);
	WORD charAttributes = FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_INTENSITY;
	int size = CONSOLE_WIDTH * CONSOLE_HEIGHT;
	fillConsoleColour(hConsole, charAttributes, size, 0 , 0);
	SetConsoleTextAttribute(hConsole, charAttributes);
}

void prestartCheck(Manipulator* tmPtr) {
		std::ofstream logFile;
		time_t timeNow;
		tm timeNowtm;
		time(&timeNow);
		char timeString[50];

		localtime_s(&timeNowtm, &timeNow);
		strftime(timeString, 50, "%a, %d %b %Y, %I:%M%p", &timeNowtm);

		logFile.open(FILENAME_LOGDEFAULT, std::ios::app);
		logFile << "\nPre-start Check for " << tmPtr->getFileName() << " started on: " << timeString << std::endl;
		
		try {
			tmPtr->sort();
			logFile << "Pre-start Check completed with no error\n." << std::endl;
			logFile.close();
		} catch(std::exception& exception) {
			std::cout << "PRE-START CHECK FAILED. " << tmPtr->getFileName() << " IS CORRUPTED. PLEASE CHECK ActivityLog.txt FOR DETAILS." << std::endl;
			logFile << "PRE-START CHECK FAILED. SEE ABOVE FOR DETAILS.\n" << std::endl;

			logFile.close();

			system("pause");
			exit(0);
		}	
}

void resizeConsole(HANDLE hConsole, SHORT newWidth, SHORT newHeight) {
	
	CONSOLE_SCREEN_BUFFER_INFO csbi; 
	SMALL_RECT maxWindowCoordinates;
	COORD maxScreenBufferSize;
	COORD newScreenBufferSize;
	int currentBufferWidth;
	int currentBufferHeight;

	GetConsoleScreenBufferInfo(hConsole, &csbi);
	
	maxScreenBufferSize = GetLargestConsoleWindowSize(hConsole);
	
	maxWindowCoordinates.Top = (SHORT) 0;
	maxWindowCoordinates.Left   = (SHORT) 0;
	maxWindowCoordinates.Right  = (SHORT) (min(newWidth, maxScreenBufferSize.X) - 1);
	maxWindowCoordinates.Bottom = (SHORT) (min(newHeight, maxScreenBufferSize.Y) - 1);
	
	newScreenBufferSize.X = newWidth;
	newScreenBufferSize.Y = newHeight;
	
	currentBufferWidth = csbi.dwSize.X;
	currentBufferHeight = csbi.dwSize.Y;

	SetConsoleWindowInfo(hConsole, TRUE, &maxWindowCoordinates);
	SetConsoleScreenBufferSize(hConsole, newScreenBufferSize);
	SetConsoleWindowInfo(hConsole, TRUE, &maxWindowCoordinates);
}

void fillConsoleColour(HANDLE hConsole, WORD colour, int length, int xPos, int yPos) {
	COORD coordScreen;
	coordScreen.X = xPos;
	coordScreen.Y = yPos;
	LPDWORD lpDWord;
	lpDWord = new DWORD;

	FillConsoleOutputAttribute(hConsole, colour, length, coordScreen, lpDWord);
}

void clearScreen(HANDLE hConsole) {
    COORD pos = {0, 0};
    DWORD written;
    CONSOLE_SCREEN_BUFFER_INFO csbi;
    unsigned size;

	GetConsoleScreenBufferInfo(hConsole, &csbi);

    size = csbi.dwSize.X * csbi.dwSize.Y;

	FillConsoleOutputCharacter(hConsole, 0, size, pos, &written);
	SetConsoleCursorPosition(hConsole, pos);
}